using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Html;
using System.Runtime.CompilerServices;
using Actors;
using Awaiter;
using Nsb;
using Nsb.Classes;
using Parse.Classes;
using ThemeChain;
using Yoshi.Fb;
using jQueryApi;

namespace Yoshi.Classes
{
    [IgnoreNamespace]
    public static class Master
    {
        public static readonly FbDataStore FbData = new FbDataStore();
        public static readonly ProfileData Profile = new ProfileData();
        public static IPage Page;
        public static FbUser FbUser;
        private const string LoadRetryKey = "LoadRetry";
        public const string PageMasterId = "PageMaster";
        private static jQueryEvent _lastMouseDn;
#if DEBUG
        public const string DebugExt = ".debug";
#else
        public const string DebugExt = "";
#endif
        public static string YoshiFlowCheck = Uri.Scripts("YoshiFlowCheck") + DebugExt + ".js";

        [PreserveCase, PreserveName]
        public static void MasterPage()
        {
            Flops();
            const bool debugging = false;
            Inform.PassExceptions = debugging;
            Inform.Tracing = debugging;
            Inform.LogActions = debugging;
            Inform.Debugging = true;
            ThemeBase.DynamicPaging = Uri.End(Window.Location.Href) == ThemeYoshi.MasterPage;
            Storage.SetLocal(ThemeBase.DevServerKey, ThemeBase.DevServer);

#if DEBUG
            Storage.SetLocal("Debug", true);
#else
            Storage.SetLocal("Debug", false);
#endif

            ThemeBase.GodMode = (bool)Storage.GetLocal("GodMode");
            FileBlobBase.Password = Script.Value((string)Storage.GetLocal(ThemeYoshi.CuratePasswordKey), (string)Storage.GetLocal(ThemeBase.DevPasswordKey));

            Inform.Log("MasterPage() Initialization");
            Window.Onerror =
                delegate(string msg, string url, int line)
                {
                    try
                    {
                        // Make sure the page exists in the page table or else set up default page
                        bool ic = Script.IsValue(ThemeYoshi.Pages.pages[ThemeYoshi.CurrentPageName]);
                        if (!ic)
                            throw new Exception("");
                    }
                    catch
                    {
                        Storage.SetSession(ThemeYoshi.PageAttrKey, ThemeYoshi.DefaultPageClass);
                        Storage.SetSession(ThemeYoshi.PageNameKey, ThemeYoshi.DefaultPageName);
                    }
                    Storage.RemoveSession(ThemeBase.PageActionKey); // On uncaught errors - cancel page actions
                    string m = Log(msg, url, line);
                    Inform.Error("Wow - something went wrong!\n\n{0}", m);
                    if (!(bool)Storage.GetLocal("GodMode"))
                    {
                        Ask.Ok(new Dictionary("Msg", String.Format("Wow - something went wrong!<br><br><small>{0}</small><br><br>Press OK to reload the page.", m), "Complete", (Action)ThemeYoshi.ReloadPage));
                    }
                    return true;
                }
                ;
#if DEBUG
            string dle = (string)Storage.GetLocal("DataLossError");
            if (!String.IsNullOrEmpty(dle) && ThemeBase.DevServer)
            {
                throw new Exception(dle);
            }
#endif

            if (!ThemeBase.DynamicPaging)
            {
                InitStaticPage();
                return;
            }
            if ((bool)Storage.GetSession("cmdReloadPage"))
            {
                Storage.RemoveSession("cmdReloadPage");
                ThemeYoshi.ReloadPage();
            }

            jQuery.FromHtml("<div id='" + PageMasterId + "'/>").AppendTo(Document.Body);
            string pageClass = Uri.QueryData.ContainsKey("pageClass") ? (string)Uri.QueryData["pageClass"] : Script.Value((string)Storage.GetSession(ThemeYoshi.PageAttrKey), ThemeYoshi.DefaultPageClass);
            string pageName = Uri.QueryData.ContainsKey("page") ? (string)Uri.QueryData["page"] : Script.Value((string)Storage.GetSession(ThemeYoshi.PageNameKey), ThemeYoshi.DefaultPageName);
            GoPage(pageClass, pageName);
            AddEvents();
        }

        [PreserveName, PreserveCase]
        [AlternateSignature]
        public static extern void GoPage(string pageClass);

        [PreserveName, PreserveCase]
        public static void GoPage(string pageClass, string pageName)
        {
            new Eyes();
            pageClass = pageClass.Trim();
            pageName = pageName ?? "Home";
            //Inform.Debug("GoPage({0}) from page {1}", page, Page != null ? Page.GetType().FullName : "none");
            if (Page != null)
                Page.Destroy();

            ThemeBase.Publishing = (bool)Storage.GetLocal(ThemeYoshi.PublishingKey);
            jQuery.Select("#" + PageMasterId).Attribute("class", "").Empty();

            Await awx = new Await();

            string[] parts = pageClass.Split(".");
            string pageLib = Uri.Scripts("/" + parts[0] + DebugExt + ".js");

            if (parts.Length > 1)
            {
                awx
                    .LoadScript(Uri.Versionize(pageLib));
            }
            awx
                .AddDx(delegate
                       {
                           jQueryObject pgMaster = jQuery.Select("#" + PageMasterId);
                           Css.ResetCss(pgMaster);
                           if (!ThemeBase.Publishing)
                               pgMaster.Hide();
                       })
                .HandleDl(Exceptions.BlobLoadError,
                          delegate
                          {
                              if (!(bool)Storage.GetLocal(LoadRetryKey))
                              {
                                  Storage.RemoveLocal(LoadRetryKey);
                                  jQuery.FromHtml("<h3/>").Html("<center>Unable to load " + pageLib + "</center>").AppendTo(Document.Body);
                                  // Dead end here, no Done() is done
                              }
                              else
                              {
                                  Storage.SetLocal(LoadRetryKey, true);
                                  Storage.RemoveSession(ThemeYoshi.PageAttrKey);
                                  //ThemeYoshi.ReloadPage();
                              }
                          })
                .AddFn(InitPage, pageClass, pageName)
                .Commit();
        }

        private static void InitPage(string pageClass, string pageName)
        {
            Storage.RemoveLocal(LoadRetryKey);
            if (pageClass.IndexOf(".html") > 0)
            {
                Window.Location.Href = pageClass + "?u=" + (new Date()).GetTime();
                return;
            }
            Debug.Assert(Type.GetType(pageClass) != null, String.Format("Type.GetType({0}) != null", pageClass));
            if (Type.IsClass(Type.GetType(pageClass)))
            {
                Storage.SetSession(ThemeYoshi.PageAttrKey, pageClass);
                Storage.SetSession(ThemeYoshi.PageNameKey, pageName);
                ThemeYoshi.CurrentPageClass = pageClass;
                ThemeYoshi.CurrentPageName = pageName;
                ThemeYoshi.GoPage = GoPage;
                ThemeBase.Update();
                FileBlobBase.SvCnt++;
                Page = (IPage)Script.Eval("new " + pageClass + "()");
                MetaBarRefresh();
                DomElement.ScrollToPosAw(Await.AsyncAw, 0);
            }
            else
                throw new Exception(Exceptions.InvalidPageException);
            //Script.Literal("meh");
        }

        [PreserveName, PreserveCase]
        public static void DoMethod(string name)
        {
            try
            {
                string[] p = name.Split(".");
                if (!(bool)Script.Literal("window[{0}][{1}]", p[0], p[1]))
                    throw new Exception("Invalid DoMethod " + name);
                Script.Eval(name + "()");
            }
            catch (Exception ex)
            {
                Inform.Error(ex.ToString());
                Cluster.FlashWindow();
            }
        }

        private static void InitStaticPage()
        {
            AddEvents();
            ThemeYoshi.PageDone = true;
        }

        [AlternateSignature]
        public static extern void Log(string msg);

        public static string Log(string msg, string url, int line)
        {
            url = url ?? "";
            line = Script.IsValue(line) ? line : 0;
            Dictionary d = new Dictionary("time", (new Date()).GetTime(), "msg", msg, "url", url, "line", line);
            List<Dictionary> log = (List<Dictionary>)Storage.GetLocal("ErrorLog") ?? new List<Dictionary>();
            log.Add(d);
            Storage.SetLocal("ErrorLog", log);
            msg = msg + "\n" + Uri.Path(url) + ":" + line;
            Inform.Error(msg);
            return msg;
        }

        public static void KeysDownEv(jQueryEvent e)
        {
            if (e.Which == 16 /*shift*/)
                ThemeBase.ShiftKey = true;
            else if (e.Which == 113 /*F2*/)
            {
                DomElement.Trigger("F2Event", e);
                DomElement.CancelEvent(e);
            }
            else if (e.Which == 114 /*F3*/)
            {
                DomElement.CancelEvent(e);
                if (Storage.GetLocal("DevPassword") == null)
                    return;
                new Await()
                .LoadScript(Uri.ScriptsNd("Scripts/ParseXmlLib.js"))
                .LoadScript(Uri.Scripts("ParseXml.js"))
                .LoadScript(Uri.Scripts("Scripts/ParseXmlClean.js"))
                .AddDx(
                    delegate
                    {
                        ParseXmlOptions opt = new ParseXmlOptions();
                        opt.Filter = delegate(ParseXml po, string cls)
                                     {
                                         if (po.GetAttr("id") == "CurateElement")
                                             return true;
                                         if (((RegularExpression)Script.Literal(@"/\b(Cover|CoverInner)\b/")).Test(cls))
                                             return true;
                                         return false;
                                     };
                        //string ot = ParseXml.Transform(jQuery.Select("html").GetHtml(), opt);
                        string ot = (string)Script.Literal("HtmlEmail.ToEmail({0}, {1})", jQuery.Select("html").GetHtml(), opt);
                        jQuery.FromHtml("<div/>").AppendTo(Document.Body).Html(Transform.Htmlize(ot)).CSS(new Dictionary("color", "white"));
                        Inform.Debug(ot);
                    })
                .Commit();
            }
            else if (e.Which == 115 /*F4*/)
            {
                if (ThemeBase.DevServer)
                    jQuery.Document.Trigger("MBToggle");
            }
            else if (e.Which == 116 /* F5*/)
            {
                Storage.RemoveSession(ThemeBase.PageActionKey);
                ThemeYoshi.SetDevRefresh();
                ThemeYoshi.RebuildPackageEv(e);
            }
            else if (e.Which == 71 /*g*/&& e.CtrlKey)
            {
                if (Storage.GetLocal("DevPassword") != null)
                    new GodMode().AwEv(Await.AsyncAw, e);
            }
        }

        public static void KeysUpEv(jQueryEvent e)
        {
            if (e.Which == 16 /*shift*/)
                ThemeBase.ShiftKey = false;
        }

        public static void AlivenessCheck(Action fn)
        {
            new Await()
                .LoadScript(Uri.Versionize(YoshiFlowCheck))
                .AddDx(delegate { Script.Literal("new AlivenessCheck({0})", fn); })
                .Commit();
        }

        public static void MetaBarRefresh()
        {
            try
            {
                Script.Literal("MetaCluster.StaticRefresh()");
            }
            catch
            {
            }
        }

        public static void MouseUpEv(jQueryEvent e)
        {
            string selected = DomElement.GetSelectedText().Trim();
            try
            {
                if (_lastMouseDn != null && e.TimeStamp - _lastMouseDn.TimeStamp > 700 && !e.ShiftKey && !e.CtrlKey && !e.AltKey && !String.IsNullOrEmpty(selected))
                    Script.Literal("AlivenessFeed.captureFeedEv({0})", e);
            }
            catch
            {
            }
        }

        public static void MouseDnEv(jQueryEvent e)
        {
            _lastMouseDn = e;
        }

        public static void PagesEv(jQueryEvent e)
        {
            try
            {
                if (!e.ShiftKey && !ThemeYoshi.SupressCurateEvents)
                    Script.Literal("Pages.Ev({0})", e);
            }
            catch
            {
            }
        }

        [AlternateSignature]
        public static extern void BadUrlEv(jQueryEvent e);

        public static void BadUrlEv(jQueryEvent e, string url)
        {
            DomElement.CancelEvent(e);
            Ask.Ok(String.Format("Unrecognized location \"{0}\"", (url ?? "")));
        }

        public static void AddEvents()
        {
            jQuery.Document
                .Bind("keydown.master", KeysDownEv)
                .Bind("keyup.master", KeysUpEv)
                .Bind("mouseup.master", MouseUpEv)
                .Bind("mousedown.master", MouseDnEv)
                .Bind("contextmenu", PagesEv)
                .Bind(ThemeYoshi.UnknownUrlEv, BadUrlEv)
                ;
        }

        /// <summary>
        /// The relative speed of this computer.
        /// </summary>
        /// <remarks>
        /// The count of how many cycles it takes for Date.GetTime() to change in a tight while loop.
        /// </remarks>
        /// <returns>relative speed</returns>
        public static void Flops()
        {
#if DEBUG
            int a = 0;
            int b = 0;
            int y;
            int x = (new Date()).GetTime();
            while ((y = (new Date()).GetTime()) == x) a++; // Clear to a time transition
            while ((new Date()).GetTime() == y) b++; // Count cycles to next transition
            Script.Literal("if (window.console) console.log('Computer speed clear=' + {0} + ' flops=' + {1})", a, b);
#endif
        }
    }
}